using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Drawing.Drawing2D;

namespace Camalon.Drawing.Shapes
{
	public enum CircleType
	{
		Star1,
		Star2,
		Star3,
		Gear1,
		Gear2,
		Gear3
	}

	
	public enum BorderType
	{
		Line,
		Curve,
		Bezier
	}

	[ToolboxItem(true)]
    [Designer("Camalon.Drawing.Design.MagicShapeDesigner, Camalon.Drawing.Design", typeof(IDesigner))]
    public class MagicShape : Shape
	{
//		public Star(System.ComponentModel.IContainer container):this()
//		{
//			///
//			/// Required for Windows.Forms Class Composition Designer support
//			///
//			container.Add(this);
//
//		}

		public MagicShape():base( new Rectangle( Point.Empty, new Size( 300,300)))
		{
			_inSize = SizeScale.Empty;
		}

		CircleType _peakStyle = CircleType.Star1;
		[Browsable( true )]
		[DefaultValue( CircleType.Star1 )]
		public CircleType PeakStyle
		{
			get
			{
				return _peakStyle ;
			}
			set
			{
				_peakStyle = value;
				ResetPath();
			}
		}
		
		BorderType _borderType = BorderType.Curve;

		[Browsable( true )]
		[DefaultValue( BorderType.Curve )]
		public BorderType BorderType
		{
			get
			{
				return _borderType ;
			}
			set
			{
				_borderType = value;
				ResetPath();
			}
		}


		
		private int _peakCount = 10;

		[DefaultValue( 10 )]
		public int PeakCount
		{
			get	{ return _peakCount; }

			set
			{
				if( _peakCount == value) return;

				if( value < 3 ) value = 3;

				_peakCount = value;
				ResetPath();
			}

		}
		

		private float _tension = 1.0f;
		[DefaultValue(1.0f)]
		[TypeConverter( typeof( PercentConverter))]
		public float CurveTension
		{	
			get{ return _tension;}
			set
			{
				//if( value < 0) value = 0;
				if( value == _tension) return;
				_tension = value;
				ResetPath();
			}

		}


		private bool _fitToBounds;

		[DefaultValue( false)]
		public bool FitToBounds
		{
			get{ return _fitToBounds;}
			set
			{ 
				if( _fitToBounds == value) return;
				_fitToBounds = value;
				ResetPath();
			}
		}



		private SizeScale _inSize = SizeScale.Empty;

		[DefaultValue( typeof(SizeScale),"Empty")]// "Empty")]
		public SizeScale InSize
		{
			get{ return _inSize;}
			set
			{
				if( _inSize == value) return;
				_inSize = value;
				ResetPath();

			}
		}

		[Browsable( false)]
		[DesignerSerializationVisibility( DesignerSerializationVisibility.Hidden)]
		public override PointF[] ControlPoints
		{
			get
			{ 
				RectangleF bounds	= base.Bounds;
				PointF center		= RectangleUtil.CenterPoint( bounds );
				SizeF inSize		= new SizeF( bounds.Width* _inSize.Width, bounds.Height* _inSize.Height); 
				PointF ptX		= new PointF( center.X + (inSize.Width/2f), center.Y);
				PointF ptY		= new PointF(center.X, center.Y + (inSize.Height/2f));

				return new PointF[]{ptX, ptY}; 
			}
			set
			{
				if( value == null || value.Length <2) 
					throw new InvalidOperationException( "Control Points array must contain only 2 points");

				RectangleF bounds	= base.Bounds;
				PointF center		= RectangleUtil.CenterPoint( bounds );
				PointF ptX = value[0];
				PointF ptY = value[1];

				bool xNegative = (center.X > ptX.X);
				bool yNegative = (center.Y > ptY.Y);

				float absX =System.Math.Abs( System.Math.Abs( center.X ) - System.Math.Abs( ptX.X ));
				float absY =System.Math.Abs( System.Math.Abs( center.Y ) - System.Math.Abs( ptY.Y ));
				
				if( xNegative) absX*=-1;
				if( yNegative) absY*=-1;


				SizeScale sizeScale = new SizeScale();

				sizeScale.Width = 2f*absX/ bounds.Width;
				sizeScale.Height = 2f*absY/ bounds.Height;

				_inSize = sizeScale;

				ResetPath();
			}
		}

		protected override GraphicsPath MakePath(RectangleF bounds)
		{
			RectangleF rect = bounds;

			PointF center	= RectangleUtil.CenterPoint( bounds);
			SizeF inBound   = new SizeF( bounds.Width* _inSize.Width, bounds.Height* _inSize.Height); 
			SizeF outBound  = rect.Size;
			
			PointF[] points = PickCirclePoints( center, inBound,0,_peakCount,outBound,_peakStyle);//Inverted
			GraphicsPath path = new GraphicsPath();

			path.StartFigure();
			
			
			if( _borderType == BorderType.Curve)
			{
				//ADD Closed curve
				path.AddClosedCurve(points,_tension);
			}
			else if( _borderType == BorderType.Line)
			{
				path.AddLines(points);
			}
			else
			{
				//DRAW BEZIER
				//-----------------------------------------------------------------------------
				PointF[] bezPoints= null;
				if( points.Length > 4)
				{
					int mod = (points.Length-1) % 3;
					if( mod == 0) 
						bezPoints= points;
					else
					{
						if( mod == 0) bezPoints=points;
						else
						{
							bezPoints= new PointF[points.Length + (3-mod)];
							points.CopyTo( bezPoints,0);
							bezPoints[bezPoints.Length-1] = points[0];
							if( mod == 2 )
								bezPoints[bezPoints.Length-2] = points[points.Length-1];
						}

					}
					path.AddBeziers(bezPoints);
//					float flatness = _tension;
//					if( flatness <0.001f) flatness = 0.001f;
//					path.Flatten( null, flatness );

				}
				//-------------------------------------------------------------------------------
			}

			if( _fitToBounds && bounds.Width != 0 && bounds.Height != 0 )
			{
				QuickWarper qw = new QuickWarper( RectangleUtil.GetPoints( bounds ), path.GetBounds(), WarpMode.Bilinear);
				path = qw.WarpPath( path );
			}

			path.CloseAllFigures();

			return path;
		}


		protected internal static PointF PickCirclePoint( PointF center, SizeF size, float angle)
		{
			/*			Circle formel : r^2 = ( x - center_x)^2 + ( y - center_y)^2
			 *							x(angle) = r.Cos(angle)
			 *							y(angle) = r.Sin(angle)
			*/
			
			double radian = Math.PI * angle / 180.0;

			float x = size.Width/2 * (float)Math.Cos(radian);
			float y = size.Height/2 * (float)Math.Sin(radian);
			
			x += center.X;
			y += center.Y;

			return new PointF( x,y );
		}

		
		protected internal static PointF[] PickCirclePoints( PointF center, SizeF size, float startAngle, int edgeCount)
		{
			float angle = 360.0f/edgeCount;
			PointF[] points = new PointF[edgeCount];

			for( int i= 0; i < edgeCount; i++)
				points[i] = PickCirclePoint(center,size, startAngle + i*angle );

			return points;
		}

		
		protected internal static PointF[] PickCirclePoints( PointF center, SizeF size, float startAngle, int edgeCount, SizeF inSize, CircleType circle)
		{
			float angle = 360.0f/edgeCount;
			PointF[] points = null;

			if( circle == CircleType.Star1 )
			{
				points = new PointF[2*edgeCount];

				for( int i= 0; i < edgeCount; i++)
				{
					points[2*i]		= PickCirclePoint(center,inSize, startAngle + i*angle );
					points[2*i+1]	= PickCirclePoint(center, size, startAngle + i*angle + (angle/2));
				}
			}
			else	if( circle == CircleType.Star2 )
			{
				points = new PointF[2*edgeCount];

				for( int i= 0; i < edgeCount; i++)
				{
					points[2*i]		= PickCirclePoint(center,inSize, startAngle + i*angle );
					points[2*i+1]	= PickCirclePoint(center, size, startAngle + i*angle);
				}
			}
			else	if( circle == CircleType.Star3 )
			{
				points = new PointF[3*edgeCount];

				for( int i= 0; i < edgeCount; i++)
				{
					points[3*i]		= PickCirclePoint(center, inSize, startAngle + i*angle +(angle/4) );
					points[3*i+1]	= PickCirclePoint(center, size, startAngle + i*angle + (angle/2));
					points[3*i+2]	= PickCirclePoint(center, inSize, startAngle + i*angle + (3*angle/4));
				}
			}
			else	if( circle == CircleType.Gear1)
			{
				points = new PointF[3*edgeCount];
				for( int i= 0; i < edgeCount; i++)
				{
					points[3*i]		= PickCirclePoint(center, inSize, startAngle + i*angle);
					points[3*i + 1]	= PickCirclePoint(center, size,   startAngle + i*angle + (angle/4) );
					points[3*i + 2]	= PickCirclePoint(center, size,   startAngle + i*angle + (3*angle/4));
				}

			}
			else	if( circle == CircleType.Gear2)
			{
				points = new PointF[4*edgeCount];
				for( int i= 0; i < edgeCount; i++)
				{
					points[4*i]		= PickCirclePoint(center,size, startAngle + i*angle);
					points[4*i+1]	= PickCirclePoint(center,size, startAngle + i*angle + (angle/2) );
					points[4*i+2]	= PickCirclePoint(center, inSize, startAngle + i*angle + (angle/2));
					points[4*i+3]	= PickCirclePoint(center, inSize, startAngle + i*angle + angle);

				}
			}
			else	if( circle == CircleType.Gear3)
			{
				points = new PointF[4*edgeCount];
				for( int i= 0; i < edgeCount; i++)
				{
					points[4*i]		= PickCirclePoint(center, inSize, startAngle + i*angle);
					points[4*i+1]	= PickCirclePoint(center, size, startAngle + i*angle + (angle/4) );
					points[4*i+2]	= PickCirclePoint(center, size, startAngle + i*angle + (angle/2));
					points[4*i+3]	= PickCirclePoint(center, inSize, startAngle + i*angle + (3*angle/4));
				}
			}


			return points;
		}

	}


}
